﻿using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;

namespace DotNetCommon;

/// <summary>
/// 一个通用的线程安全的对象池定义
/// <example>
/// <para>示例代码: </para>
/// <code>
/// public class Program
/// {
///     public static void Main(string[] args)
///     {
///         //配置并创建对象池
///         var pool = new EasyPool&lt;Person>(
///             () => new Person(),//Person实例创建方法
///             person => person.Init(),//对象池回收Person执行的逻辑
///             10//池中最大的数量
///         );
///         
///         //从对象池中取一个person实例
///         var person = pool.Get();
///         //do something
///         //将person实例归还到对象池中
///         pool.Return(person);
///     }
/// }
/// </code>
/// </example>
/// </summary>
public interface IEasyPool<T> : IDisposable where T : class
{
    /// <summary>
    /// 当前池中的模型数量
    /// </summary>
    int Count { get; }

    /// <summary>
    /// 从对象池中获取模型
    /// </summary>
    T Get();

    /// <summary>
    /// 将模型归还到对象池中(可指定是否需要执行预设的重置逻辑)
    /// </summary>
    /// <param name="item">要规划的模型</param>
    /// <param name="reset">
    /// 是否需要执行预设的重置逻辑
    /// </param>
    /// <returns>归还成功返回True，否则返回False(比如：池中的模型数量已达最大值)</returns>
    bool Return(T item, bool reset = true);
}

/// <summary>
/// 一个通用的线程安全的对象池定义
/// <example>
/// <para>示例代码: </para>
/// <code>
/// public class Program
/// {
///     public static void Main(string[] args)
///     {
///         //配置并创建对象池
///         var pool = new EasyPoolAsync&lt;Person>(
///             () => Task.FromResult(new Person()),//Person实例创建方法
///             person => person.InitAsync(),//对象池回收Person执行的逻辑
///             10//池中最大的数量
///         );
///         
///         //从对象池中取一个person实例
///         var person = await pool.GetAsync();
///         try
///         {
///             //do something
///         }
///         finnaly
///         {
///             //将person实例归还到对象池中
///             await pool.ReturnAsync(person);
///         }
///     }
/// }
/// </code>
/// </example>
/// </summary>
public interface IEasyPoolAsync<T> : IDisposable where T : class
{
    /// <summary>
    /// 当前池中的模型数量
    /// </summary>
    int Count { get; }

    /// <summary>
    /// 从对象池中获取模型
    /// </summary>
    Task<T> GetAsync();

    /// <summary>
    /// 将模型归还到对象池中(可指定是否需要执行预设的重置逻辑)
    /// </summary>
    /// <param name="item">要归还的模型</param>
    /// <param name="reset">
    /// 是否需要执行预设的重置逻辑
    /// </param>
    /// <returns>归还成功返回True，否则返回False(比如：池中的模型数量已达最大值)</returns>
    Task<bool> ReturnAsync(T item, bool reset = true);
}

///<inheritdoc cref="IEasyPool{T}" />
public sealed class EasyPool<T> : IEasyPool<T>, IEasyPoolAsync<T> where T : class
{
    private readonly ConcurrentBag<T> _pool;
    private readonly Func<T> _factory;
    private readonly Func<Task<T>> _factoryAsync;
    private readonly Action<T> _reset;
    private readonly Func<T, Task> _resetAsync;
    private readonly int _maxCount;

    /// <summary>
    /// 创建一个<see cref="EasyPool{T}"/>实例
    /// </summary>
    /// <param name="factory">创建<typeparamref name="T"/>实例的工厂方法</param>
    /// <param name="reset">将对象归还到池子前对对象执行的清理逻辑</param>
    /// <param name="maxCount">最多缓存的数量</param>
    public EasyPool(Func<T> factory, Action<T> reset, int maxCount)
    {
        AssertUtil.NotNull(factory);
        _factory = factory;
        _reset = reset;
        _maxCount = maxCount;

        _pool = [];
    }

    /// <summary>
    /// 创建一个<see cref="EasyPool{T}"/>实例
    /// </summary>
    /// <param name="factory">创建<typeparamref name="T"/>实例的工厂方法</param>
    /// <param name="reset">将对象归还到池子前对对象执行的清理逻辑</param>
    /// <param name="maxCount">最多缓存的数量</param>
    public EasyPool(Func<Task<T>> factory, Func<T, Task> reset, int maxCount)
    {
        AssertUtil.NotNull(factory);
        _factoryAsync = factory;
        _resetAsync = reset;
        _maxCount = maxCount;

        _pool = [];
    }

    /// <summary>
    /// 获取池子中对象的数量
    /// </summary>
    public int Count => (int)_pool.Count;

    /// <summary>
    /// 从对象池中获取一个实例
    /// </summary>
    public T Get()
    {
        if (_pool.TryTake(out T item)) return item;
        if (_factory != null) return _factory();
        else return _factoryAsync().Result;
    }

    /// <summary>
    /// 从对象池中获取一个实例
    /// </summary>
    public async Task<T> GetAsync()
    {
        if (_pool.TryTake(out T item)) return item;
        if (_factoryAsync != null) return await _factoryAsync();
        else return _factory();
    }

    /// <summary>
    /// 将实例归还到池子中,对象成功入池返回True,否则返回False(对象池中的实例数量已达最大值)
    /// </summary>
    /// <param name="item">要归还的实例</param>
    /// <param name="reset">
    /// 这个实例是否需要执行清理逻辑
    /// </param>
    /// <returns></returns>
    public bool Return(T item, bool reset = true)
    {
        if (reset)
        {
            if (_reset != null) _reset(item);
            else if (_resetAsync != null) _resetAsync(item).Wait();
        }
        if (_pool.Count >= _maxCount)
        {
            //未成功归还，尝试触发Dispose方法
            if (item is IDisposable t) { try { t.Dispose(); } catch { }; }
            else if (item is IAsyncDisposable t2) try { t2.DisposeAsync().AsTask().Wait(); } catch { };
            return false;
        }

        _pool.Add(item);
        return true;
    }

    /// <summary>
    /// 将实例归还到池子中,对象成功入池返回True,否则返回False(对象池中的实例数量已达最大值)
    /// </summary>
    /// <param name="item">要归还的实例</param>
    /// <param name="reset">
    /// 这个实例是否需要执行清理逻辑
    /// </param>
    /// <returns></returns>
    public async Task<bool> ReturnAsync(T item, bool reset = true)
    {
        if (reset)
        {
            if (_resetAsync != null) await _resetAsync.Invoke(item);
            else _reset?.Invoke(item);
        }
        if (_pool.Count >= _maxCount)
        {
            //未成功归还，尝试触发Dispose方法
            if (item is IAsyncDisposable t) try { await t.DisposeAsync(); } catch { }
            else if (item is IDisposable t2) try { t2.Dispose(); } catch { }
            return false;
        }

        _pool.Add(item);
        return true;
    }

    /// <summary>
    /// 释放池子中的实例
    /// </summary>
    public void Dispose()
    {
        while (_pool.TryTake(out T item))
        {
            if (item is IDisposable t) try { t.Dispose(); } catch { }
            else if (item is IAsyncDisposable t2) try { t2.DisposeAsync().AsTask().Wait(); } catch { }
        }
    }
    /// <summary>
    /// 释放池子中的实例
    /// </summary>
    public async Task DisposeAsync()
    {
        while (_pool.TryTake(out T item))
        {
            if (item is IAsyncDisposable t) try { await t.DisposeAsync(); } catch { }
            else if (item is IDisposable t2) try { t2.Dispose(); } catch { }
        }
    }
}